﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using TugasAkhir.Models;
using TugasAkhir.Objects;

namespace TugasAkhir.Helpers
{
    public class HitungAnalisis
    {
        private DataSet hasilQueryData;
        private DataSet hasilQueryPertanyaan;
        private DataSet hasilQueryFuzzy;

        private int totalPopulasi;
        private int totalPertanyaan;

        private int jumlahPengisian;

        private int[,] dataInt;
        private double[,] dataDouble;

        private double[] hasilValiditas;
        private double hasilReliabilitas;
        private double[] hasilSkor;
        private double[] hasilFuzzy;
        private int hasilSample;

        private double[] nilaiAkhir;
        private int[] kehadiran;

        private string tahun;
        private string periode;
        private string matakuliah;

        private List<int> nonValid;
        private List<int> nonReliabel;

        public List<int> NonValid
        {
            get { return this.nonValid; }
        }

        public List<int> NonReliabel
        {
            get { return this.nonReliabel; }
        }

        public string Tahun
        {
            get { return this.tahun; }
        }

        public int Sample
        {
            get { return this.hasilSample - this.jumlahPengisian; }
        }

        public string GetKeteranganPengaruh(int index)
        {
            int max = this.hasilFuzzy.Length;
            if (index >= 0 && index < max)
            {
                if (hasilFuzzy[index] >= 81.0)
                {
                    return "Sangat Mempengaruhi Sekali";
                }
                else if (hasilFuzzy[index] >= 71.0 && hasilFuzzy[index] < 81.0)
                {
                    return "Sangat Mempengaruhi";
                }
                else if (hasilFuzzy[index] >= 66.0 && hasilFuzzy[index] < 71.0)
                {
                    return "Mempengaruhi";
                }
                else if (hasilFuzzy[index] >= 61.0 && hasilFuzzy[index] < 66.0)
                {
                    return "Cukup Mempengaruhi";
                }
                else if (hasilFuzzy[index] >= 56.0 && hasilFuzzy[index] < 61.0)
                {
                    return "Cukup Sedikit Mempengaruhi";
                }
                else if (hasilFuzzy[index] >= 41.0 && hasilFuzzy[index] < 56.0)
                {
                    return "Sedikit Mempengaruhi";
                }
                else if (hasilSkor[index] > 0.0 && hasilSkor[index] < 41.0)
                {
                    return "Sangat Sedikit Mempengaruhi";
                }
                else
                {
                    return "Tidak Mempengaruhi";
                }
            }
            else
            {
                return string.Empty;
            }
        }


        public string GetKeteranganSkor(int index)
        {
            int max = this.hasilSkor.Length;
            if (index >= 0 && index < max)
            {
                if (hasilSkor[index] >= 81.0)
                {
                    return "Sangat Baik Sekali";
                }
                else if (hasilSkor[index] >= 71.0 && hasilSkor[index] < 81.0)
                {
                    return "Baik Sekali";
                }
                else if (hasilSkor[index] >= 66.0 && hasilSkor[index] < 71.0)
                {
                    return "Baik";
                }
                else if (hasilSkor[index] >= 61.0 && hasilSkor[index] < 66.0)
                {
                    return "Cukup Baik";
                }
                else if (hasilSkor[index] >= 56.0 && hasilSkor[index] < 61.0)
                {
                    return "Cukup";
                }
                else if (hasilSkor[index] >= 41.0 && hasilSkor[index] < 56.0)
                {
                    return "Kurang";
                }
                else if (hasilSkor[index] > 0.0 && hasilSkor[index] < 41.0)
                {
                    return "Kurang Sekali";
                }
                else
                {
                    return "Tidak Terdapat Skor";
                }
            }
            else
            {
                return string.Empty;
            }
        }
        public HitungAnalisis(string tahun, string periode, string matakuliah, string path)
        {
            this.tahun = tahun;
            this.periode = periode;
            this.matakuliah = matakuliah;
            RTabel.Instance.SetPath(path);

            QueryPertanyaan();
            QueryNilai();
            QueryFuzzy();
            QuerySample();
        }

        #region Queries
        private void QueryPertanyaan()
        {
            this.hasilQueryPertanyaan = new DataSet();

            if (PerhitunganModel.GetTabelPertanyaan(this.matakuliah, this.tahun, this.periode, out this.hasilQueryPertanyaan))
            {
                var items = this.hasilQueryPertanyaan.Tables[0].Rows;

                this.totalPertanyaan = items.Count;

                this.hasilValiditas = new double[this.totalPertanyaan];
                this.hasilSkor = new double[this.totalPertanyaan];
            }
        }

        private void QueryNilai()
        {
            this.hasilQueryData = new DataSet();

            if (PerhitunganModel.GetTabelNilai(this.matakuliah, this.tahun, this.periode, out this.hasilQueryData))
            {
                var items = this.hasilQueryData.Tables[0].Rows;

                /* mendapatkan total populasi */
                this.totalPopulasi = items.Count;

                this.dataInt = new int[this.totalPopulasi, this.totalPertanyaan];
                this.dataDouble = new double[this.totalPopulasi, this.totalPertanyaan];

                /* pengisian jawaban */
                for (int i = 0; i < this.totalPopulasi; i++)
                {
                    string[] num = items[i][4].ToString().Split(new char[] { ',' });

                    for (int j = 0; j < num.Length; j++)
                    {
                        this.dataInt[i, j] = int.Parse(num[j]);
                        this.dataDouble[i, j] = double.Parse(num[j]);
                    }
                }
            }
        }

        private void QueryFuzzy()
        {
            this.nilaiAkhir = new double[this.totalPopulasi];
            this.kehadiran = new int[this.totalPopulasi];

            this.hasilFuzzy = new double[this.totalPertanyaan];

            if (PerhitunganModel.GetNilaiAkhir(this.matakuliah, this.tahun, this.periode, out this.hasilQueryFuzzy))
            {
                for (int i = 0; i < hasilQueryFuzzy.Tables[0].Rows.Count; i++)
                {
                    var item = hasilQueryFuzzy.Tables[0].Rows[i];
                    this.kehadiran[i] = Convert.ToInt32(item[0].ToString());
                    this.nilaiAkhir[i] = Convert.ToDouble(item[1].ToString());
                }
            }
        }

        private void QuerySample()
        {
            PerhitunganModel.GetJumlahPengisi(this.matakuliah, this.tahun, this.periode, out this.jumlahPengisian);
        }
        #endregion

        public void Hitung()
        {
            HitungValiditas();
            HitungReliabilitas();
            HitungSkor();
            HitungFuzzy();
            HitungSample();

            Simpan();
        }

        private void HitungValiditas()
        {
            var koefisien = new KoefisienKorelasi[this.totalPertanyaan];
            var Y = new List<int>();
            var Xs = new List<int>[this.totalPertanyaan];

            this.nonValid = new List<int>();

            // init masing-masing X
            for (int i = 0; i < this.totalPertanyaan; i++)
            {
                Xs[i] = new List<int>();
            }

            // dapatkan Y
            for (int i = 0; i < this.totalPopulasi; i++)
            {
                int y = 0;
                for (int j = 0; j < this.totalPertanyaan; j++)
                {
                    y += dataInt[i, j];
                }

                Y.Add(y);
            }

            for (int i = 0; i < this.totalPertanyaan; i++)
            {
                koefisien[i] = new KoefisienKorelasi();
                koefisien[i].UkuranPopulasi = this.totalPopulasi;

                for (int j = 0; j < totalPopulasi; j++)
                {
                    Xs[i].Add(this.dataInt[j, i]);
                }

                foreach (var item in Xs[i])
                    koefisien[i].TambahSkorItem(item);

                foreach (var item in Y)
                    koefisien[i].TambahSkorTotalItem(item);

                this.hasilValiditas[i] = koefisien[i].HitungKoefisienKorelasi();
                if (this.hasilValiditas[i] < RTabel.Instance[this.totalPopulasi - 2])
                {
                    this.nonValid.Add(i);
                }
            }
        }

        private void HitungReliabilitas()
        {
            var koefisien = new KoefisienReliabilitas(this.totalPopulasi, this.totalPertanyaan, this.dataInt);
            this.hasilReliabilitas = koefisien.HitungKoefisienReliabilitas(this.nonValid);

            this.nonReliabel = new List<int>();

            for (int i = 0; i < this.totalPertanyaan; i++)
            {
                if (this.nonValid.Contains(i))
                {
                    this.nonReliabel.Add(i);
                    continue;
                }

                else if (this.hasilReliabilitas < 0.6)
                {
                    this.nonReliabel.Add(i);
                }
            }
        }

        private void HitungSkor()
        {
            var skor = new Skor(this.totalPopulasi, this.totalPertanyaan, this.dataInt, 4);
            var hasil = skor.HitungSkor();

            for (int i = 0; i < hasil.Count; i++)
            {
                if (!nonValid.Contains(i) && !nonReliabel.Contains(i))
                {
                    this.hasilSkor[i] = hasil[i]; 
                }
                else
                {
                    this.hasilSkor[i] = 0.0;
                }
            }
        }

        private void HitungFuzzy()
        {
            var A = new List<double>();
            var AKehadiran = new List<double>();

            var fuzzyCalc = new FuzzyCalculation(this.totalPopulasi, this.totalPertanyaan);
            fuzzyCalc.InitKehadiran(this.kehadiran);
            fuzzyCalc.InitNilai(this.nilaiAkhir);
            fuzzyCalc.InitA(this.dataDouble);
            fuzzyCalc.Calculate(out A, out AKehadiran);

            for (int i = 0; i < A.Count; i++)
            {
                if (!nonValid.Contains(i) && !nonReliabel.Contains(i))
                {
                    this.hasilFuzzy[i] = A[i];
                }
                else
                {
                    this.hasilFuzzy[i] = 0.0;
                }
                
            }
        }

        private void HitungSample()
        {
            this.hasilSample = (int)Math.Ceiling((double)this.totalPopulasi / (1.0 + ((double)this.totalPopulasi * Math.Pow(0.05, 2))));
        }

        private void Simpan()
        {
            for (int i = 0; i < this.totalPertanyaan; i++)
            {
                var analisis = new AnalisisObject();
                analisis.Tahun = this.tahun;
                analisis.Periode = this.periode;
                analisis.Matakuliah = this.matakuliah;
                analisis.Pertanyaan = this.hasilQueryPertanyaan.Tables[0].Rows[i][0].ToString();
                analisis.Validitas = this.hasilValiditas[i];
                if (this.nonValid.Contains(i))
                    analisis.Reliabilitas = 0.0;
                else
                    analisis.Reliabilitas = this.hasilReliabilitas;
                analisis.Pengaruh = this.hasilFuzzy[i];
                analisis.Skor = this.hasilSkor[i];

                AnalisisModel.Perbarui(analisis);
            }
        }
    }
}